package com.hd.trans.ui.component;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.provider.MediaStore;
import android.util.LongSparseArray;
import android.util.SparseArray;
import android.webkit.MimeTypeMap;

import androidx.annotation.RequiresApi;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;

import com.hd.trans.R;
import com.hd.trans.common.Constants;
import com.hd.trans.db.bean.TranslateRecord;
import com.hd.trans.network.ParamJson;
import com.hd.trans.network.PreferenceMgr;
import com.hd.trans.utils.DownloadUtils;
import com.hd.trans.utils.FileUtil;
import com.hd.trans.utils.FileUtils;
import com.hd.trans.utils.PcmToWav;
import com.hd.trans.utils.StringUtils;
import com.hd.trans.utils.VoicePlaySettingUtil;
import com.orhanobut.logger.Logger;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.observers.DisposableObserver;
import io.reactivex.schedulers.Schedulers;

/**
 * Author:  yang
 * Date:  2020-01-17 02:08
 * Desc:  互盾语音TTS下载并合成组件
 */
public class HdTtsDownloadComponent implements TtsDownloadComponent, LifecycleObserver {

    //    private List<Disposable> mDisposables;
    private LongSparseArray<Disposable> mDisposables;

    private boolean isDestroy = false;


    private Handler mHandler;

    public HdTtsDownloadComponent() {
        mHandler = new Handler(Looper.getMainLooper());
    }

    /**
     * 页面销毁 终止回调
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void destroy() {
        isDestroy = true;
        if (mHandler != null) {
            mHandler.removeCallbacks(null);
        }
        stopAllDownload();
    }

    public boolean isDestroy() {
        return isDestroy;
    }

    /**
     * 下载合成音频文件
     */
    public void getTtsFile(String ttsCode, String ttsContent, String wavPath, TtsDownloadCallback callback){
        if(ttsCode == null)return;
        if(ttsContent == null)return;
        if(wavPath == null)return;

//        String stringMd5 = FileMd5.getMD5String(ttsContent);
//        Log.i("liaoyucheng",stringMd5);
        long triggerTime = System.currentTimeMillis();
        mDisposables = new LongSparseArray<>();
        String[] contentList = StringUtils.splitStringByLength(ttsContent, Constants.MAX_TTS_SIZE);

        if (contentList != null && contentList.length > 0) {

            int size = contentList.length;
            Observable<?>[] observables = new Observable[size];

            for (int i = size - 1; i >= 0; i--) {
                int finalI = i;
                observables[i] = DownloadUtils.getInstance().getDownloadService()
                        .downloadVoice(getParamBy(ttsCode,contentList,i))
                        .map(data -> new MultTtsResponse(String.valueOf(finalI), data.byteStream(),
                                FileUtils.getPcmFileAbsolutePath(System.currentTimeMillis() + "_" + finalI)));
            }

            Observable.mergeArray(observables)
                    .subscribeOn(Schedulers.io())
                    .observeOn(Schedulers.computation()) // 用于计算任务
                    .doOnNext(object -> writeFile(object, triggerTime, callback))
                    .observeOn(Schedulers.io())
                    .subscribe(new Observer<Object>() {

                        private SparseArray<String> fileMap = new SparseArray<>();

                        @Override
                        public void onSubscribe(Disposable d) {
                            if (isDestroy)
                                return;
                            mDisposables.put(triggerTime, d);
                        }

                        @Override
                        public void onNext(Object response) {
                            if (isDestroy || mDisposables.get(triggerTime) == null)
                                return;
                            if (response instanceof MultTtsResponse) {
                                MultTtsResponse multResponseBody = (MultTtsResponse) response;
                                int index = StringUtils.toInt(multResponseBody.getRequestTag(), -1);
                                String fileName = multResponseBody.getTempFileName();
                                fileMap.put(index, fileName);
                            }

                        }

                        @Override
                        public void onError(Throwable e) {
//                            listener.onFailure(fileName,e.getMessage());
                            if (isDestroy || mDisposables.get(triggerTime) == null)
                                return;
                            mHandler.post(() -> {
                                if (callback != null) {
                                    callback.onDownloadFailed(Constants.ResponseCode.ERROR_SERVER, "下载失败");
                                }
                            });

                        }

                        @Override
                        public void onComplete() {
                            if (isDestroy || mDisposables.get(triggerTime) == null)
                                return;
                            List<String> pcmPaths = new ArrayList<>();
                            if (fileMap.size() > 0) {
                                for (int i = 0; i < size; i++) {
                                    pcmPaths.add(fileMap.get(i));
                                }
                                //全部下载完成,开始合并(合并之前现将其它语速的TTS文件删除)
                                if (wavPath.length() > 7) {
                                    String commonStr = wavPath.substring(0, wavPath.length() - 7);
                                    for (int i = Constants.VOICE_SPEED_MIN; i <= Constants.VOICE_SPEED_MAX; i++) {
                                        String fileStr = String.format(Locale.getDefault(), "%s_%02d.wav", commonStr, i);
                                        if (fileStr.equals(wavPath))
                                            continue;
                                        FileUtil.deleteFile2(new File(fileStr));
                                    }
                                }
                                mergeVoiceFile(pcmPaths, wavPath, triggerTime, callback);
                            } else {
                                mHandler.post(() -> {
                                    if (callback != null) {
                                        callback.onDownloadFailed(Constants.ResponseCode.ERROR_SERVER, "下载失败");
                                    }
                                });
                            }
                        }

                    });

        }
    }


    @Override
    public void getTtsFile(final TranslateRecord record, boolean isTransPart, String wavPath, TtsDownloadCallback callback) {
        if (record == null)
            return;
        final long triggerTime = record.getTriggerTime();
//        mDisposables = new ArrayList<>();
        mDisposables = new LongSparseArray<>();

        String ttsContent = isTransPart?record.getContentTranslate().trim():record.getContentOrig().trim();

//        String stringMd5 = FileMd5.getMD5String(ttsContent);
//        Log.i("liaoyucheng",stringMd5);
        String ttsCode = null;
        if(isTransPart){
            ttsCode = record.getLanguageTo().getTtsCode();
        }else {
            ttsCode = record.getLanguageFrom().getTtsCode();
        }

        String[] contentList = StringUtils.splitStringByLength(ttsContent, Constants.MAX_TTS_SIZE);

        if (contentList != null && contentList.length > 0) {

            int size = contentList.length;
            Observable<?>[] observables = new Observable[size];

            for (int i = size - 1; i >= 0; i--) {
                int finalI = i;
                observables[i] = DownloadUtils.getInstance().getDownloadService()
                        .downloadVoice(getParamBy(ttsCode,contentList,i))
                        .map(data -> new MultTtsResponse(String.valueOf(finalI), data.byteStream(),
                                FileUtils.getPcmFileAbsolutePath(System.currentTimeMillis() + "_" + finalI)));
            }

            Observable.mergeArray(observables)
                    .subscribeOn(Schedulers.io())
                    .observeOn(Schedulers.computation()) // 用于计算任务
                    .doOnNext(object -> writeFile(object, triggerTime, callback))
                    .observeOn(Schedulers.io())
                    .subscribe(new Observer<Object>() {

                        private SparseArray<String> fileMap = new SparseArray<>();

                        @Override
                        public void onSubscribe(Disposable d) {
                            if (isDestroy)
                                return;
                            mDisposables.put(triggerTime, d);
//                            mDisposables.add(d);
                        }

                        @Override
                        public void onNext(Object response) {
                            if (isDestroy || mDisposables.get(record.getTriggerTime()) == null)
                                return;
                            if (response instanceof MultTtsResponse) {
                                MultTtsResponse multResponseBody = (MultTtsResponse) response;
                                int index = StringUtils.toInt(multResponseBody.getRequestTag(), -1);
                                String fileName = multResponseBody.getTempFileName();
//                                Logger.i("========= 下载完成==== index  " + index);
//                                Logger.i("========= 下载完成==== fileName  " + fileName);
                                fileMap.put(index, fileName);
                            }

                        }

                        @Override
                        public void onError(Throwable e) {
//                            listener.onFailure(fileName,e.getMessage());
                            if (isDestroy || mDisposables.get(record.getTriggerTime()) == null)
                                return;
                            mHandler.post(() -> {
                                if (callback != null) {
                                    callback.onDownloadFailed(Constants.ResponseCode.ERROR_SERVER, "下载失败");
                                }
                            });

                        }

                        @Override
                        public void onComplete() {
                            Logger.i("=========全部下载完成 ");
                            if (isDestroy || mDisposables.get(record.getTriggerTime()) == null)
                                return;
                            List<String> pcmPaths = new ArrayList<>();
                            if (fileMap.size() > 0) {
                                for (int i = 0; i < size; i++) {
                                    pcmPaths.add(fileMap.get(i));
                                }
                                //全部下载完成,开始合并(合并之前现将其它语速的TTS文件删除)
//                                Logger.i("===============================wavPath :"+wavPath );
                                if (wavPath.length() > 7) {
                                    String commonStr = wavPath.substring(0, wavPath.length() - 7);
                                    Logger.i("=");
                                    for (int i = Constants.VOICE_SPEED_MIN; i <= Constants.VOICE_SPEED_MAX; i++) {
                                        String fileStr = String.format(Locale.getDefault(), "%s_%s_%02d.wav", String.valueOf(isTransPart),commonStr, i);
                                        if (fileStr.equals(wavPath))
                                            continue;
                                        FileUtil.deleteFile2(new File(fileStr));
//                                        Logger.i("===============================deleteFile : "+res+" : "+fileStr);
//                                        FileUtil.deleteFileSafely(fileStr);
                                    }
                                }
                                mergeVoiceFile(pcmPaths, wavPath, triggerTime, callback);
                            } else {
                                mHandler.post(() -> {
                                    if (callback != null) {
                                        callback.onDownloadFailed(Constants.ResponseCode.ERROR_SERVER, "下载失败");
                                    }
                                });
                            }
                        }

                    });

        }

    }

    private ParamJson.DownloadVoiceParam getParamBy(String ttsCode,String[] contentList,int i){
        String[] voiceAnchor = VoicePlaySettingUtil.getVoiceAnchor(ttsCode);
        if(voiceAnchor.length>=3){
            return new ParamJson.DownloadVoiceParam.Builder()
                    .audiotype("pcm")
                    .speed(PreferenceMgr.getInstance().getTranslatePreference().getVoiceSpeed())
                    .text(contentList[i])
                    .textlanguage(ttsCode)
                    .voicetag(voiceAnchor[0])
                    .voicegender(voiceAnchor[1])
                    .build();
        }else if(voiceAnchor.length>=2){
            return new ParamJson.DownloadVoiceParam.Builder()
                    .audiotype("pcm")
                    .speed(PreferenceMgr.getInstance().getTranslatePreference().getVoiceSpeed())
                    .text(contentList[i])
                    .textlanguage(ttsCode)
                    .voicetag(voiceAnchor[0])
                    .build();
        }else {
            return new ParamJson.DownloadVoiceParam.Builder()
                    .audiotype("pcm")
                    .speed(PreferenceMgr.getInstance().getTranslatePreference().getVoiceSpeed())
                    .text(contentList[i])
                    .textlanguage(ttsCode)
                    .build();
        }
    }



    /**
     * 适配Android
     */
    @RequiresApi(30)
    public static boolean storeMusicApi30(Context context, String path, String title) {

        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        ContentResolver resolver = context.getContentResolver();
        try {
            retriever.setDataSource(path);
            int duration = Integer.parseInt(
                    retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)); //读取持续时间
            File file = new File(path);
            ContentValues values = new ContentValues();
            String extension = MimeTypeMap.getFileExtensionFromUrl(path);
            String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
            values.put(MediaStore.Audio.Media.MIME_TYPE, mime);
//            values.put(MediaStore.Audio.Media.DATA, path); // 文件路径
            values.put(MediaStore.Audio.Media.RELATIVE_PATH, "Music/ve");
            values.put(MediaStore.Audio.Media.TITLE, title);
            values.put(MediaStore.Audio.Media.ARTIST, context.getString(R.string.sdk_name));
            values.put(MediaStore.Audio.Media.DISPLAY_NAME, file.getName());// 文件名称
            values.put(MediaStore.Audio.Media.SIZE, file.length());
            values.put(MediaStore.Audio.Media.DURATION, duration); // 播放时间
            Uri insertUri = resolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
            OutputStream out = null;
            FileInputStream fis = null;
            try {
                // First, write the actual data for our screenshot
                out = resolver.openOutputStream(insertUri);
                fis = new FileInputStream(path);
                int length = 0;
                byte[] buffer = new byte[1000];
                while ((length = fis.read(buffer, 0, buffer.length)) != -1) {
                    out.write(buffer, 0, length);
                }
                out.flush();
                // Everything went well above, publish it!
                values.clear();
                values.put(MediaStore.MediaColumns.IS_PENDING, 0);
                values.putNull(MediaStore.MediaColumns.DATE_EXPIRES);
                resolver.update(insertUri, values, null, null);
            } catch (IOException ex) {
                resolver.delete(insertUri, null, null);
                return false;
            } finally {
                if (out != null) {
                    out.close();
                }
                if (fis != null) {
                    fis.close();
                }
            }
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        } finally {
            try {
                retriever.release();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }





    /**
     * 删除TTS文件
     *
     * @param record
     */
    @Override
    public void deleteTtsFile(TranslateRecord record, boolean isTransPart, Observer<Boolean> observer) {
        Observable.create((ObservableOnSubscribe<Boolean>) emit -> {
            boolean res = false;
            String wavTime = String.valueOf(record.getTriggerTime());
            for (int i = Constants.VOICE_SPEED_MIN; i <= Constants.VOICE_SPEED_MAX; i++) {
                String fileStr = FileUtils.getWavFileAbsolutePath(String.format(Locale.getDefault(), "%s_%s_%02d.wav", isTransPart,wavTime, i));
                res = FileUtil.deleteFile2(new File(fileStr));
//           FileUtil.deleteFileSafely(fileStr);
            }
            emit.onNext(res);
            emit.onComplete();
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
    }

    /**
     * 删除所有TTS文件
     */
    @Override
    public void deleteAllTtsFiles(Observer<Boolean> observer) {
        Observable.create((ObservableOnSubscribe<Boolean>) emit -> {
            FileUtils.deleteAllTtsFiles();
            emit.onNext(true);
            emit.onComplete();
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);

    }

    /**
     * 停止所有下载任务
     */
    @Override
    public void stopAllDownload() {
        if (mDisposables != null) {
            for (int i = 0; i < mDisposables.size(); i++) {
//                long key = mDisposables.keyAt(i);
                Disposable disposable = mDisposables.valueAt(i);
                if (disposable != null && !disposable.isDisposed()) {
                    disposable.dispose();
                }
            }
            mDisposables.clear();
        }
    }

    /**
     * 停止下载
     */
    @Override
    public void stopDownload(TranslateRecord record) {
        if (mDisposables != null && record != null) {
            Disposable disposable = mDisposables.get(record.getTriggerTime());
            if (disposable != null && !disposable.isDisposed()) {
                disposable.dispose();
            }
            mDisposables.remove(record.getTriggerTime());
        }
    }

    /**
     * 合并tts文件
     *
     * @param pcmPaths
     * @param wavPath
     * @param callback
     */
    private void mergeVoiceFile(List<String> pcmPaths, String wavPath, final long triggerTime, TtsDownloadCallback callback) {
        Observable.create((ObservableOnSubscribe<Boolean>) emit -> {
            if (PcmToWav.mergePCMFilesToWAVFile(pcmPaths, wavPath)) {
                //操作成功
                emit.onNext(true);
            } else {
                emit.onNext(false);
            }
            emit.onComplete();
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
//               .compose(bindToLifecycle())
                .subscribe(new DisposableObserver<Boolean>() {
                    @Override
                    public void onNext(Boolean result) {
                        if (!isDestroy && mDisposables.get(triggerTime) != null) {
                            if (result) {
                                if (callback != null) {
                                    callback.onCompleted();
                                }
                            } else {
                                if (callback != null) {
                                    callback.onMergeFailed(Constants.ResponseCode.ERROR_SERVER, "语音合成失败");
                                }
                            }
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        if (callback != null && !isDestroy && mDisposables.get(triggerTime) != null) {
                            callback.onMergeFailed(Constants.ResponseCode.ERROR_SERVER, "语音合成失败");
                        }
                    }

                    @Override
                    public void onComplete() {

                    }
                });

    }


    /**
     * 将输入流写入文件
     */
    private void writeFile(Object object, final long triggerTime, TtsDownloadCallback callback) {
        if (isDestroy || mDisposables.get(triggerTime) == null)
            return;
        if (object instanceof MultTtsResponse) {
            MultTtsResponse response = (MultTtsResponse) object;
            FileOutputStream fos;
            try {
                fos = new FileOutputStream(response.getTempFileName());
                byte[] b = new byte[1024];
                int len;
                while ((len = response.getInputStream().read(b)) != -1) {
                    fos.write(b, 0, len);
                }
                response.getInputStream().close();
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();

                mHandler.post(() -> {
                    if (callback != null && !isDestroy && mDisposables.get(triggerTime) != null) {
                        callback.onDownloadFailed(Constants.ResponseCode.ERROR_COMM, "文件写入失败");
                    }
                });
            }
        }
    }


}
